/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "subcmd.h"

#define USER_LEN   10
#define MAX_LEN    10
#define NSLOTS_LEN 10
#define PEND_LEN   5
#define RUN_LEN    5
#define SSUSP_LEN  5
#define USUSP_LEN  5
#define RSV_LEN    5

static json *userinfo_2json(struct QuickPoolUserInfo *);
static void listuser_usage(const char *);

/**
 * @brief       qp user list handler, to query user information from js.
 *
 * @param[in]   argc          #1: number of input argument
 * @param[in]   argv          #2: string array of the input argument
 *
 * @retval      0             #1: succeed
 * @retval      1             #2: input option is wrong or kind of input argument not specified
 * @retval      2             #3: argument has syntax error
 * @retval      3             #4: failed to query user information
 * @retval      4             #5: not enough memory to print json string
 */
int
cmd_list_users(int argc, char **argv)
{
    bool_t fOpt = FALSE;
    bool_t wOpt = FALSE;
    
    while (TRUE) {
        int option_index = 0;
        
        static struct option long_options[] = {
            {"help", no_argument, 0, 'h'},
            {"format", no_argument, 0, 'f'},
            {"wide", no_argument, 0, 'w'},
            {NULL, 0, 0, 0}
        };

        int opt;
        opt = getopt_long(argc, argv, "h:fw", long_options, &option_index);
        if (-1 == opt) {
            break;
        }

        switch (opt) {
        case 'h':
            listuser_usage(argv[0]);
            exit(0);
        case 'f':
            fOpt = TRUE;
            break;
        case 'w':
            wOpt = TRUE;
            break;
        case '?':
        default:
            listuser_usage(argv[0]);
            exit(1);
        }
    }

    // if not specify node name, set all to TRUE
    int all=FALSE;
    unsigned int numUser;
    char **users=NULL, **userPoint=NULL;
    numUser = get_argument_names(argc, argv, optind, &users, &all, "user");
    if (!all && !numUser) {
        numUser = 1;
    } else {
        userPoint = users;
    }

    struct request userRequest;
    memset(&userRequest, 0, sizeof(struct request));

    userRequest.keyvalues = (struct keyvalue *)calloc(2, sizeof(struct keyvalue));
    if (NULL == userRequest.keyvalues) {
        fprintf(stderr, "calloc(%ld) failed due to %m.\n", 2*sizeof(struct keyvalue));
        exit(4);
    }

    unsigned int n=0;
    userRequest.keyvalues[n].key = REQUEST_KEY_NUMBER;
    userRequest.keyvalues[n].value = (char *)calloc(12, sizeof(char));
    if (NULL == userRequest.keyvalues[n].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
        exit(4);
    }
    snprintf(userRequest.keyvalues[n].value, 12, "%d", numUser);
    n ++;

    if (1 == numUser && NULL == userPoint) { // not specify user, query myself
        userRequest.keyvalues[n].key = REQUEST_KEY_NAMES;
        userRequest.keyvalues[n].value = getmy_uname();
        n ++;
    } else if (0 < numUser) {

        // to query specific users.
        userRequest.keyvalues[n].key = REQUEST_KEY_NAMES;
        unsigned int i, size = 0;
        for (i = 0; i < numUser; i++) {
            size += strlen(users[i]) + 1;
        }

        userRequest.keyvalues[n].value = (char *)calloc(size, sizeof(char));
        if (NULL == userRequest.keyvalues[n].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", size);
            exit(4);
        }

        for (i = 0; i < numUser; i++) {
            if (i == 0) {
                strcpy(userRequest.keyvalues[n].value, userPoint[i]);
            } else {
                strcat(userRequest.keyvalues[n].value, " ");
                strcat(userRequest.keyvalues[n].value, userPoint[i]);
            }
        }
        n ++;
    }
    userRequest.number = n;

    FREEUP(users);

    quickpool_errno = 0;
    struct QuickPoolUserReply *reply;
    reply = client_get_userinfo(&userRequest, NULL);
    if (quickpool_errno != 0 || NULL == reply) {

        if (NULL == reply) {
            fprintf(stderr, "Fail to query user list due to above error.\n");
            gf_xdr_free(gf_xdr_request, &userRequest);
            exit(3);
        }

        // JS replied error message.
        fprintf(stderr, "%s\n", reply->error_message);

        gf_xdr_free(libxdr_user_reply, reply);
        FREEUP(reply);
        gf_xdr_free(gf_xdr_request, &userRequest);

        exit(3);
    }
    gf_xdr_free(gf_xdr_request, &userRequest);
    
    /*option -f or --format args*/
    char buf[4096+1] = {0};
    char header[4096+1] = {0};
    char str1[20+1] = {0};
    
    if ((fOpt | wOpt) && reply->num_user > 0){
        snprintf(buf, 4096, "%%-%ds  %%%ds  %%%dd  %%%dd  %%%dd  %%%dd  %%%dd  %%%dd\n",
                 USER_LEN, MAX_LEN, NSLOTS_LEN, PEND_LEN, RUN_LEN, SSUSP_LEN, USUSP_LEN, RSV_LEN);
        snprintf(header, 4096, "%%-%ds  %%%ds  %%%ds  %%%ds  %%%ds  %%%ds  %%%ds  %%%ds\n",
                 USER_LEN, MAX_LEN, NSLOTS_LEN, PEND_LEN, RUN_LEN, SSUSP_LEN, USUSP_LEN, RSV_LEN);
        fprintf(stdout,header, "USER/GROUP", "MAX", "NSLOTS", "PEND", "RUN", "SSUSP", "USUSP", "RSV");
        
        for(n = 0; n < reply->num_user; n++){
            if (fOpt && !wOpt && strlen(reply->users[n].user) > USER_LEN){
                reply->users[n].user[USER_LEN-1] = '*';
                reply->users[n].user[USER_LEN] = '\0';
            }
           
            if (reply->users[n].max_num_slot >= INFINIT_INT){
                memset(str1, 0, sizeof(str1));
                strncpy(str1, "-", 20);
            }else{
                snprintf(str1, 20, "%d", reply->users[n].max_num_slot);
            }
             
            fprintf(stdout, buf, reply->users[n].user, str1, reply->users[n].num_job_slot,
                reply->users[n].num_pending_slot, reply->users[n].num_running_slot, reply->users[n].num_SSUSP_slot, 
                reply->users[n].num_USUSP_slot, reply->users[n].num_reserving_slot);
            
        }    
        
        gf_xdr_free(libxdr_user_reply, reply);
        FREEUP(reply);
        
        exit(0);
    }

    json *jsonUserList = json_CreateObject();
    unsigned int i;
    for (i = 0; i < reply->num_user; i++) {

        json *jsonUser = userinfo_2json(&(reply->users[i]));
        json_AddItemToObject(jsonUserList, reply->users[i].user, jsonUser);
    }

    if (0 < json_GetArraySize(jsonUserList)) {
        char *strJson;
        strJson = json_Print(jsonUserList);
        if (NULL != strJson) {
            fprintf(stdout, "%s\n", strJson);
            FREEUP(strJson);
        } else {

            perror("malloc");
            json_Delete(jsonUserList);
            gf_xdr_free(libxdr_user_reply, reply);
            FREEUP(reply);

            exit(4);
        }
    } else {
        // nothing in json means there's no user in the cluster, should not go here.
        fprintf(stderr, "No user information in the cluster.\n");
    }
    json_Delete(jsonUserList);

    gf_xdr_free(libxdr_user_reply, reply);
    FREEUP(reply);

    exit(0);
} // end function cmd_list_users

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

/**
 * @brief       transform user structure to json.
 *
 * @param[in]   user          #1: user to be tranformed
 * @param[in]   info          #3: load index and resource table
 *
 * @retval      not NULL      #1: transformed json object
 */
static json *
userinfo_2json(struct QuickPoolUserInfo *user)
{
    json *jsonUser = json_CreateObject();

    s2j_json_set_basic_element(jsonUser, user, string, "USER/GROUP", user);

    if (user->max_num_slot < INFINIT_INT) {
        s2j_json_set_basic_element(jsonUser, user, int, "MAX", max_num_slot);
    }

    s2j_json_set_basic_element(jsonUser, user, int, "NSLOTS", num_job_slot);
    s2j_json_set_basic_element(jsonUser, user, int, "PEND", num_pending_slot);
    s2j_json_set_basic_element(jsonUser, user, int, "RUN", num_running_slot);
    s2j_json_set_basic_element(jsonUser, user, int, "SSUSP", num_SSUSP_slot);
    s2j_json_set_basic_element(jsonUser, user, int, "USUSP", num_USUSP_slot);
    s2j_json_set_basic_element(jsonUser, user, int, "RSV", num_reserving_slot);

    return jsonUser;
} // end function userinfo_2json

static void
listuser_usage(const char *cmd)
{
    fprintf(stdout, "qp user %s usage: ", cmd);
    fprintf(stdout, "[-h, --help] [user_name ...] [all]\n");

    return;
}
